{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "\n",
    "import dpp\n",
    "\n",
    "from tqdm import tqdm_notebook as tqdm\n",
    "from copy import deepcopy\n",
    "# from umap import UMAP\n",
    "import seaborn as sns\n",
    "sns.set_style('whitegrid')\n",
    "torch.set_default_tensor_type(torch.cuda.FloatTensor) "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Generate synthetic data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "np.random.seed(123)\n",
    "n_samples = 1024\n",
    "n_sequences = 32\n",
    "poisson = [dpp.gen.poisson.sample(n_samples=n_samples) for _ in range(n_sequences)]\n",
    "renewal = [dpp.gen.renewal.sample(n_samples) for _ in range(n_sequences)]\n",
    "self_correcting = [dpp.gen.self_correcting.sample(n_samples) for _ in range(n_sequences)]\n",
    "hawkes1 = [dpp.gen.hawkes.hawkes1(n_samples)[0] for _ in range(n_sequences)]\n",
    "hawkes2 = [dpp.gen.hawkes.hawkes2(n_samples)[0] for _ in range(n_sequences)]\n",
    "\n",
    "all_arrival = poisson + renewal + self_correcting + hawkes1 + hawkes2\n",
    "all_deltas = [dpp.utils.get_inter_times(t) for t in all_arrival]\n",
    "\n",
    "dataset = dpp.data.SequenceDataset(all_deltas) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "d_train, d_val, d_test = dataset.train_val_test_split_each() \n",
    "mean_in, std_in = d_train.get_mean_std_in()\n",
    "d_train = d_train.normalize(mean_in, std_in)\n",
    "d_val = d_val.normalize(mean_in, std_in)\n",
    "d_test = d_test.normalize(mean_in, std_in)  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "d_train.break_down_long_sequences(128)\n",
    "collate = dpp.data.collate\n",
    "dl_train = torch.utils.data.DataLoader(d_train, batch_size=64, shuffle=True, collate_fn=collate)\n",
    "dl_val = torch.utils.data.DataLoader(d_val, batch_size=1, shuffle=False, collate_fn=collate)\n",
    "dl_test = torch.utils.data.DataLoader(d_test, batch_size=1, shuffle=False, collate_fn=collate)  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Define the model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "config = dpp.model.ModelConfig(\n",
    "    use_history=True,\n",
    "    history_size=64,\n",
    "    use_embedding=True,\n",
    "    embedding_size=64,\n",
    "    num_embeddings=len(dataset),\n",
    ")\n",
    "\n",
    "shift_init, scale_init = d_train.get_log_mean_std_out()\n",
    "decoder = dpp.decoders.LogNormMix(config, hypernet_hidden_sizes=[], \n",
    "                              shift_init=shift_init, scale_init=scale_init) \n",
    "model = dpp.model.Model(config, decoder) \n",
    "\n",
    "opt = torch.optim.Adam(model.parameters(),\n",
    "                       weight_decay=1e-5,\n",
    "                       lr=1e-3)  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_total_loss(loader):\n",
    "    loader_log_prob, loader_lengths = [], []\n",
    "    for input in loader:\n",
    "        loader_log_prob.append(model.log_prob(input).detach())\n",
    "        loader_lengths.append(input.length.detach())\n",
    "    return -model.aggregate(loader_log_prob, loader_lengths)  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "max_epochs = 1000\n",
    "display_step = 25\n",
    "patience = 100  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Training the model\n",
    "### Pre-train the embeddings"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If we immediately start training with history, the model learns to distinguish between different TPPs based on the RNN encoding of the history, and the \"static\" embeddings don't look as nice."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "model.use_history(False)\n",
    "model.use_embedding(True)  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch   25, loss_train_last_batch = 0.6578, loss_val = 0.5921\n",
      "Epoch   50, loss_train_last_batch = 0.4769, loss_val = 0.5901\n",
      "Epoch   75, loss_train_last_batch = 0.5849, loss_val = 0.5908\n",
      "Epoch  100, loss_train_last_batch = 0.5061, loss_val = 0.5914\n",
      "Epoch  125, loss_train_last_batch = 0.5089, loss_val = 0.5921\n",
      "Breaking due to early stopping at epoch 149\n"
     ]
    }
   ],
   "source": [
    "impatient = 0\n",
    "best_loss = np.inf\n",
    "best_model = deepcopy(model.state_dict())\n",
    "training_val_losses = []\n",
    "\n",
    "for epoch in range(max_epochs):\n",
    "    model.train()\n",
    "    for input in dl_train:\n",
    "        opt.zero_grad()\n",
    "        log_prob = model.log_prob(input)\n",
    "        loss = -model.aggregate(log_prob, input.length)\n",
    "        loss.backward()\n",
    "        opt.step()\n",
    "\n",
    "    model.eval()\n",
    "    loss_val = get_total_loss(dl_val)\n",
    "    training_val_losses.append(loss_val.item())\n",
    "\n",
    "    if (best_loss - loss_val) < 1e-4:\n",
    "        impatient += 1\n",
    "        if loss_val < best_loss:\n",
    "            best_loss = loss_val.item()\n",
    "            best_model = deepcopy(model.state_dict())\n",
    "    else:\n",
    "        best_loss = loss_val.item()\n",
    "        best_model = deepcopy(model.state_dict())\n",
    "        impatient = 0\n",
    "\n",
    "    if impatient >= patience:\n",
    "        print(f'Breaking due to early stopping at epoch {epoch}')\n",
    "        break\n",
    "\n",
    "    if (epoch + 1) % display_step == 0:\n",
    "        print(f\"Epoch {epoch+1:4d}, loss_train_last_batch = {loss:.4f}, loss_val = {loss_val:.4f}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Finish training with history"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "model.use_history(True)\n",
    "model.use_embedding(True)  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The training loop below is exactly the same as 3 cells above"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch   25, loss_train = 0.5782, loss_val = 0.5379\n",
      "Epoch   50, loss_train = 0.6117, loss_val = 0.5321\n",
      "Epoch   75, loss_train = 0.5734, loss_val = 0.5409\n",
      "Epoch  100, loss_train = 0.6134, loss_val = 0.5588\n",
      "Epoch  125, loss_train = 0.4832, loss_val = 0.5785\n",
      "Breaking due to early stopping at epoch 139\n"
     ]
    }
   ],
   "source": [
    "model.load_state_dict(best_model)\n",
    "impatient = 0\n",
    "best_loss = np.inf\n",
    "best_model = deepcopy(model.state_dict())\n",
    "\n",
    "for epoch in range(max_epochs):\n",
    "    model.train()\n",
    "    for input in dl_train:\n",
    "        opt.zero_grad()\n",
    "        log_prob = model.log_prob(input)\n",
    "        loss = -model.aggregate(log_prob, input.length)\n",
    "        loss.backward()\n",
    "        opt.step()\n",
    "\n",
    "    model.eval()\n",
    "    loss_val = get_total_loss(dl_val)\n",
    "    training_val_losses.append(loss_val.item())\n",
    "\n",
    "    if (best_loss - loss_val) < 1e-4:\n",
    "        impatient += 1\n",
    "        if loss_val < best_loss:\n",
    "            best_loss = loss_val.item()\n",
    "            best_model = deepcopy(model.state_dict())\n",
    "    else:\n",
    "        best_loss = loss_val.item()\n",
    "        best_model = deepcopy(model.state_dict())\n",
    "        impatient = 0\n",
    "\n",
    "    if impatient >= patience:\n",
    "        print(f'Breaking due to early stopping at epoch {epoch}')\n",
    "        break\n",
    "\n",
    "    if (epoch + 1) % display_step == 0:\n",
    "        print(f\"Epoch {epoch+1:4d}, loss_train = {loss:.4f}, loss_val = {loss_val:.4f}\")  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Visualize the learned embeddings"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "from matplotlib.colors import ListedColormap\n",
    "cmap = ListedColormap(sns.color_palette('colorblind', 5).as_hex())\n",
    "colors = np.zeros(n_sequences * 5)\n",
    "for i in range(5):\n",
    "    colors[np.arange(i*n_sequences, (i + 1)*n_sequences)] = i\n",
    "\n",
    "def plot_embeddings():\n",
    "    from sklearn.manifold import TSNE\n",
    "\n",
    "    if config.embedding_size == 2:\n",
    "        z = model.embedding.weight.cpu().detach().numpy()\n",
    "    else:\n",
    "        x = model.embedding.weight.cpu().detach().numpy()\n",
    "        # z = UMAP().fit_transform(x)\n",
    "        z = TSNE().fit_transform(x)\n",
    "\n",
    "    fig, ax = plt.subplots(figsize=(3.8, 2.5))\n",
    "    scatter = ax.scatter(z[:, 0], z[:, 1], c=colors, alpha=0.5, cmap=cmap)\n",
    "    a, b = scatter.legend_elements()\n",
    "    legend1 = ax.legend(a, ['Poisson', 'Renewal', 'Self-correcting', 'Hawkes1', 'Hawkes2'], bbox_to_anchor=(1.0, 1.0))\n",
    "    ax.set_yticklabels([])\n",
    "    ax.set_xticklabels([])\n",
    "    ax.set_xlabel('t-SNE component 1')\n",
    "    ax.set_ylabel('t-SNE component 2')\n",
    "    plt.tight_layout()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAQoAAACsCAYAAACD+eKOAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjMsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+AADFEAAAgAElEQVR4nO3dd3wVZb748c/MnJbegFACgkpAAih9XYorRVdJCE1l9aKuKKCrImsBXVGuF9eC5dpW9y7C6s+KCrLAoi4qFkQkiNJVWhoQ0nN6mXl+fxxyJEByAqSckOf9euVFTpmZ75yQb57+KEIIgSRJUh3U5g5AkqTIJxOFJElhyUQhSVJYMlFIkhSWTBSSJIVlauoL/vDDD1it1lM6xuv1nvIxzam1xev1ernooosaMCIp0jR5orBarVxwwQWndMyuXbtO+Zjm1Nri3bVrVwNGI0UiWfWQJCmsJi9RSJFPCMGOisNsOHIAXQiGtD2HC5M7oCry70prJROFhN/QWZm3g88O7cFv6Ggo5DsrMKkqZlXj66L9jOrQnRu7D0JRlOYOV2oGMlG0ckII3juym9wSLwoK+Y5yvi8rRFVUOsckoikKsWYrHx/8iUs6nMe5cSn1Oq/f76egoACPx9PIdyA1BJvNRlpaGmaz+aSvy0TRyhW6KtlSeYR84STfWYHD70U/+pq78ghdYpOo8Hk44CjnwwPbuO2CodhMJ//PdKyCggLi4uLo2rWrLIVEOCEEpaWlFBQU0K1bt5O+R1Y6W7kil52f3GUUOCvxBfRQkgDwCYM99lLynOUUue08v/Mr7tn0L8q9rrDn9Xg8pKSkyCTRAiiKQkpKSp2lP5koWrGAofNjeSGHvQ78egCvCJz0fQIICINKr5tP8n9mRe72ep1fJomWI9zPSiaKVspv6Ly482tW5O5AAVxGACPMMQ7dR4G7gvcO/NgUIUoRRCaKVuqH0kK2lB3k3LgUVEWhPouSaIqCELCnqqTR42sIF1xwAdnZ2WRmZnLnnXfidrvrfP+UKVOaKLKWRyaKVurH8oPEmMyUeJyIeqUJEAKEMFBp+CrF1oOVzP/4J2565wfmf/wTWw9WnvE5bTYbK1asYNWqVZjNZt5555063x/u9dZMJopWKtZkJWAYHHJX4RPhKh1BAUSw4csWQ0MujLb1YCVPf7GPcreftEQb5W4/T3+xr0GSRbWBAweSm5sLwJIlS8jMzCQzM5N//vOfoff069cPgCNHjnDdddeFSiM5OTnous7cuXPJzMwkKysrdNyuXbu4+uqrycrK4k9/+hOVlcGYp06dysKFC5k8eTKXX345OTk5DXYvzUEmilbqt+26EhAGfkPHX89EAaApKjbVxDdFBxoslmXbDpMYZSYpyoyqKCRFmUmMMrNs2+EGOX8gEODLL78kPT2d7du3s2zZMpYuXcq7777Le++9x86dO2u8f9WqVQwbNowVK1awYsUKevbsya5duygqKmLVqlWsXLmSiRMnAnDfffdxzz33sHLlStLT03nxxRdD59F1nffff58HHnigxvMtkUwUrVSX2CRuSf8N2tFh2fWpTChAijmK7vFt+Hdhw00Eyyt3k2CrOaQnwWYir7zuNoVwPB4P2dnZTJo0iY4dOzJ58mQ2b97M6NGjiY6OJiYmhjFjxpzw175Pnz4sW7aMF154gZ9//pnY2Fg6d+5Mfn4+//M//8OXX35JbGwsdrsdu93O4MGDAZgwYUKNc40ZMwaAjIwMCgsLz+hemluTD7jyer2nPNvQ4/G0qBmKLSXeROD6pHQ2Fh3AG7bPAzQUVBSqKquoqKxssHvskhRFudtPUtSvA7kqPQG6JEWd0Xmr2yiOVZ8q06BBg3jjjTf44osvuO+++5g2bRrjx49nxYoVfP3117z11lusWbOGBx54oM7zWCwWAFRVRdf1Ot8b6eQ080bQkuKNc3Qgds8XaCKAU/fX+V5VUbBaLETFxzKwbWcu6Bm8xzNNGBP7tOfpL/YBwZJEpSdAhdvPtMGdz+i8JzNo0CDmzp3L9OnTEUKwdu1annzyyRrvKSwsJDU1lauvvhqXy8WOHTsYMWIEFouFyy+/nC5dujB37lzi4uKIj48nJyeHgQMHsmLFCgYNGtTgMUcCOYS7lYs2W+gencR2ZwkqCkYtPSAKEBCCtJhErJqZcV0yGiyGvh0TuPuSc1m27TB55W66JEUxbXBn+nZMaLBrVMvIyGDixIlcddVVAEyePJlevXrVeM93333Hq6++islkIjo6mieeeIIjR45w//33YxjBktef//xnAJ544gkefvhh3G43nTt35rHHHmvwmCOB0tT7epzOX9uW9BcaWla8Qgj+8O9F5LiK8ekBitwO9OPShQKYFY0UWzRz+45kyrn9aWOLCb1+svttSZ+BFFTXz0yWKFo5RVG4JLEL273lxJosuPUAzoAXYRjBBKGZSbTaGNWhO6CQ1SWjRpKQWgeZKCT6xacyUO+MAvRIbMd+exl7KotRFYUBbbowoE0nqvwe4s1RdI5JbO5wpWYgu0clEkxWpqUPRlUUojULvZPaM6BtGv3bdKZTTDyH3XaiNAu39xoqV7lqpWSJQgJgePvz6JHQjm1lhxBARlJ7ojQz+x1l2DQT3ePbYFK15g5TaiZ1JgqHw0FZWRldunSp8fzu3bvp2bNnowYmNb12UXGM6hRX47l+1k7NFI0USWotR/773//m97//PXfccQdjx45l69atodfuv//+JglOkqTIUGui+Pvf/86yZctYsWIFjz32GPfddx+ffPIJUL/RbZLU3I6dZj5z5kyqqqqaO6SQjRs3MmPGjOYOo95qrXoYhkG7du0A6Nu3L6+//jozZ87k8OHDcuUiqcF5i7fh2rOcgD0fU1xnos+fgLVtnzM657FDuOfMmcObb77Jrbfe2hDhtjq1lihiYmLIy8sLPW7Xrh2vv/46n376Kb/88kuTBCe1Dt7ibVRufgbdU44W2wndU07l5mfwFm9rsGtcdNFFFBUVhR4vWrSISZMmkZWVxfPPPw8EFwS+4oorePDBBxk7diw33XRTaB3JvLw8pk2bxsSJE7n22mvZu3cvuq4zatQohBBUVVXRs2dPNm3aBMC1115Lbm4uW7duZcqUKYwfP54pU6awb9++BrunplRropg/f/4JVYzY2FgWLVrEX//610YPTGo9XHuWo1oT0WxJKIqKZktCtSbi2rO8Qc6v6zobNmxg5MiRAHz99dfk5uby/vvvs2LFCnbs2BH6Bc/NzeW6665j9erVxMXF8fHHHwMwb9485s2bx7Jly5gzZw7//d//jaZpdO3alT179rB582YyMjLIycnB5/Nx+PBhzjnnHM4991zeeOMNPvzwQ+68806effbZBrmnplZr1aO2Xg2z2cy4ceMaLSCp9QnY89Fia/auqNYEAvb8Mzpv9TTzwsJCMjIyGDp0KADr169n/fr1jB8/HgCXy8WBAwfo0KEDaWlpoWHM1dPDnU4nW7ZsYdasWaFz+3w+ILggzqZNmygoKGDGjBksXbqUQYMG0adPsNpkt9uZM2cOubm5KIqC31/3xLtIJcdRSM3OFNc5WO2wJYWeM7yVmOLObPZodRuF3W5nxowZvPnmm1x//fUIIZg+ffoJa2QWFBSEpoYDaJqG1+tFCEF8fPwJU9YhmCjeeecdjhw5wqxZs3j11Vf57rvvQrNIn3vuOYYMGcJLL71EQUEB119//RndU3ORw+ykZhd9/gQMbwW6pxwhDHRPOYa3gujzJzTI+ePi4njwwQdZvHgxfr+fYcOG8cEHH+B0OgEoKiqitLS01uNjY2NJS0tjzZo1QLDXb/fu3QBceOGFbNmyBUVRsFqt9OzZk3fffZeBAwcCwRJFamoqAMuXN0xVqjmETRSvvfZavZ6TpNNlbduHhAF/RrMloTsK0WxJJAz48xn3ehyrV69e9OzZk9WrVzNs2DAyMzOZMmUKWVlZ3HnnnaGkUZuFCxfy/vvvM27cOMaOHcvatWuB4OI07du356KLLgKCJQyn00l6ejoAN998M8888wxTpkxp2YvXiDDGjx9/wnPZ2dnhDqvVzp07m+SY5tTa4j3Z8S3tM5Dq/pnV2kaxatUqVq1aRUFBATNnzgw973Q6SUyUMwglqTWpNVH069ePtm3bUl5ezk033RR6PiYmhh49ejRJcJIkRYZaE0WnTp3o1KkT7777blPGI0lSBArbPfrJJ5/w1FNPUVpaihACIYKbwHz//fdNEZ8kSREgbKJYuHAhr7zyCuedd15TxCNJUgQK2z2akpIik4QktXJhE0Xv3r256667WLVqFZ988knoS5Ii3csvv8zYsWPJysoiOzubH3/8sdb3zp07l48++giAnJwcxo4dS3Z2dmhSWCQoKChg5cqVocfbtm1jwYIFTXLtsFUPp9NJVFQU69evr/H8ZZdd1mhBSa3P1rKDLD+wjTxnOV1ikpjQtQ99kzue9vm2bNnCunXrWL58ORaLhbKysnrPs/jXv/7FTTfdxKRJk077+serbt9T1V//Nuu6jqbVf3nBwsJCVq1aRVZWFhDc+rB6TkljC5soGnpDE7mlYORp7ni3lh3k6e1fkGSJIi0mkXKfm6e3f8HdvS857WRRXFxMUlJSaO5GcnIyANu3b+fxxx/H5XKRlJTEY489Flp3BeC9997jo48+4uuvv+abb77h6aefrnHekpISHn74YfLzgxPW5s+fT//+/VmyZAkffPABENxU6MYbb6SgoIBbbrmFIUOG8MMPP/DSSy+RmZnJjTfeyNdff82cOXOw2WwnjSc3N5eHH36YsrIyNE3jueee4+mnn2bv3r1kZ2czYcIELrjgAhYvXszf//53XnjhBQ4ePEhBQQEHDx7khhtuCM0reemll1i5ciUdOnQgKSmJjIwMpk2bdmofaLjRWvv27RPXX3+9GDt2rBBCiF27domXXnqpUUZ/NeQxzam1xXumIzPnb/5IzNqwXMz//qPQ16wNy8X8zR+ddkwOh0OMGzdOXHbZZeLhhx8WGzduFD6fT1xzzTWitLRUCCHE6tWrxdy5c4UQQsyZM0esWbPmhO+PN2vWLLFkyRIhhBCBQEBUVVWJbdu2iczMTOF0OoXD4RBXXnml2LFjh8jPzxc9evQQW7ZsCR2fnp4uVq9eLYQQdcYzefJk8cknnwghhPB4PMLlcolvv/1WTJ8+PXSuYx8///zz4pprrhFer1eUlpaKwYMHC5/PJ7Zu3SrGjRsn3G63sNvtYsyYMWLRokUnvbfTGplZbd68edx333089NBDQHD6+T333MNtt912ahlJkmqR5ywn7bj9QhIsNvKc5ad9zpiYGJYtW0ZOTg4bN25k9uzZ3Hrrrfz888/88Y9/BIKruLVt2/aUzvvtt9+G9irVNI24uLgaO6QDoR3SR44cSceOHUPzQKqPufzyywHYv3//SeNxOBwUFRWFdkO3Wq31iu2SSy7BYrGQnJxMcnIypaWlbN68mVGjRmGz2QC49NJLT+l+q4VNFG63m759+9Z47lTqVZIUTpeYJMp9bpKsv+5eXunz0CUmqY6jwtM0jSFDhjBkyBDS09N588036d69+ykNInz22WdZt24dwEmnmUPda8hWJ49qVqs19PsjhDhpPA6Ho97xHev4KfKBQOC0znMyYXs9kpKSyMvLC62T+dFHH51yFpakukzo2odyn5tyrxtDCMq9bsp9biZ0Pf2Gun379nHgwIHQ4127dnHeeedRVlbGli1bAPD7/WGXdZw9ezYrVqwIJYmLL76Yt956Cwg2RjocDgYNGsTatWtxu924XC7Wrl0bmmZel27dup00ntjYWNq3bx+aoerz+XC73cTExISd5Xq8/v378/nnn+P1enE6naGkd6rCligefvhh5s2bx759+xg+fDhpaWksXLjwtC4mSSfTN7kjd/e+pEavx03pg8+o18PlcrFgwQKqqqrQNI1zzjmHRx55hGuuuYYFCxZgt9vRdZ0bbriB7t271/u8f/nLX5g3bx4ffPABqqoyf/58+vXrd9Id0gsKCuo8l8Vi4fnnnz9pPE8++SQPPfQQzz33HGazmeeee44ePXqgaRrjxo1j4sSJ9doEum/fvowcOZJx48bRqVMnevfuTVxcXNjjTlBr68VxnE6nsNvt9X17rWRjZuRp7sZMqXE5HA4hhBAul0tMmDBBbN++/aTvO6PGTJ/Px8cff0xhYWGNOs/tt99+6llJkqQm99BDD7Fnzx68Xi8TJkwgIyPjlM8RNlHceuutxMXFkZGRUaOxRJKkluH4sSCnI2yiKCoq4tVXXz3jC7UGDm8AuTWSdDYKmyj69evHTz/9JBerqcMRu5fXc/LZWWQHRSFVdXF7BzeHHV7sngBpiVGclxItd1iTWqywiWLz5s0sX76cTp061ah6HDs5pTXzBnSeXLcHhzdA58QoBLBlfzkTX9tEz3axKEfLGIM6JzL94nMwa3Lhc6nlCZso/vGPfzRFHC3W9sN2Spw+uiZF4w3oHHF42V/hw4uJGIuJdrFWhBB8m1dO7w7xXHJeSnOHLEmnLOyft06dOmG32/n888/5/PPPsdvtdOrUKdxhrUaF2w8CDpQ6ef/Hg6z9uZjcSj8ldg9bD1ays8hOfoWbeKuJ9ftr3ztCanj9+vWr8XjZsmU88sgjZ3TOhjgHwLRp0xg4cGCL2dE8bInitdde47333guNO7/33nu5+uqrmTp1aqMHF8lKnF5W7TzCZ78UszG3nCMOL2ZNQVUUfIbAqwu2Hqoiv8KN22+gKAqXnp+CXzdk9eMk3HlbsW9ehr8kD3ObLsQNmEhUl77hD2yhbr75Ztxud4tZkzbs/9j333+fpUuXMmvWLGbNmsW7777L0qVLmyK2iFXp9vPo2l/4el8pHeIsVHkCOH063oDA6dPxGxAQ4PEb2L06VpOCX9fZUljJoo25dc4NaI3ceVspW/MUurMcU0oaurOcsjVP4c7b2mjX/Oyzz7jqqqsYP348N954IyUlJQBkZWVRVVWFEIIhQ4bw4YcfAsE/kN98802Nc6xbt45rrrmGsrIyysrKuOOOO5g0aRKTJk1i8+bNAHz33XdkZ2eTnZ3N+PHjQ/M4Lr74YmJiYhrt/hpavfYePXYSWGufEGYYgkUbc/liTynRVo02MWZirCbsXj9On45JBZMChggmC59uYNZV2sRYsWkKGw6Uk9WrPWmJUeEv1krYNy9DjUlCOzoJrPpf++ZlZ1SqqN6kuFplZWVoR/MBAwawdOlSFEXhvffeY9GiRcydO5d+/frx/fff07FjR9LS0sjJyWH8+PH8+OOPzJ8/n8OHDwPwn//8hyVLlvB///d/JCQkcPfdd3PDDTcwcOBADh48yLRp01izZg2LFy/moYceYsCAATidznrPBI00YRNF9Rj2MWPGIITg008/bdCVf1qa97ce5J0fDqIfXbFob4mLUpcXVyBYSvAbcGx5wRswsJmMo6sbmVBVhcN2r0wUx/CX5GFKSavxnBqdgL8k74zOW71JcbVly5axfft2AA4fPszs2bMpLi7G5/ORlha8fvXu5B07duQPf/gDS5cupaioiISEhFAJYOPGjWzfvp3FixcTGxsLwDfffMOePXtC13I4HDgcDvr378/jjz9OVlYWl112WYsqRRwrbKL44x//yODBg0NFqccee4xevXo1emCRqMzlY83uYromRbH9sJ2DlX6K7B48x2wpeXylwhDg9hv4dR+JUcGPOzna3HRBtwDmNl3QneWhkgSA4arE3KZLo11zwYIF3HjjjYwaNYqNGzfy4osvAjBo0CDeeustDh06xOzZs1m7di0fffRRjdmgnTt3Jj8/n/3794eWojMMg3fffTe07kO16dOnc8kll/DFF19w9dVXs2TJkha5WHW9W9XE0f08WnP9+nCVF0UBq0njcJWH/MqaSeJkBOAOGHgDOvvLPHRNjKZbcnTdB7UycQMmYjjL0Z1HdzN3lmM4y4kbMLHRrnnsLuPV7RAAHTp0oLy8nAMHDtC5c2f69+/P4sWLGTBgQOg9HTt25IUXXmDOnDmhaerDhg3jjTfeCL2nemnBvLw8evTowfTp0+nduzf79+9vtHtqTGETxYsvvsjcuXOpqqqivLyc+++/n7/97W9NEVvESYgyoRuCn4/YESJYWqgvQ4AuBG1jzXKE5nGiuvQl+Yp70GKSCJQWoMUkkXzFPY3a63H77bcza9Ysrr322hP20u3bty/dunUDglWRoqKiGokC4Nxzz+Wpp55i1qxZ5OXl8Ze//IXt27eTlZXFlVdeydtvvw0Eew0zMzMZN24cNpuNESNGAHDttdcya9YsNmzYwIgRI/jqq68a7V4bgiLCFBGuuOIKPvzww1AjjMfjYcKECaxZs+a0Lrhr1656zaM/02MagxCCB/+9m5e+2Y/LpxM4xcKVVVO4IDWWTbNGoEVQF+mZfr4nOz5SfmZS/dX1M6vXgCuv1xt67PP56NKl8eqOkUxRFJKiTaFxEKdSLtCARJvGvlI3Pxef3lJnktRcwjZmWiwWxo4dy9ChQ1EUhfXr1zNgwIDQxiMPPvhgowcZKXwBg51HnAzvmszn+0pw+3R8Rv2OtZgUXAGBVVPZlF/JBe3jGzdYSWpAYRPFmDFjQqMyAQYPHtyoAUUygQABvVLjqPL6+HJ//VeJ9usCXejEW03E2Vr3WBSp5QmbKCZMmNAUcbQIVpPGgLQEthys4qJOSeRWeDlS5cHlN7CaVXTdQAjwHdd2YdVAVRUCusCvGwzpcmarS0tSUwubKD7//HOee+45Dh48SCAQCHWTfv/9900RX8T5Q/9OFFZ5yC1zYQiByaTS1mrCEIIqj8B3tCvErFKjZ0QIMKsK57eJJa/CTccEOeBKajnCJoq//vWvvPDCC/To0UN26wHJ0Rb++/Ie7Cyys+S7fLYeqqLM6cVvCFx+HUMI4i0qZrMZl1/HZlIxawoxFhOJUWZ6d4hj+yE7vzknublvRZLqLWyiaN++Penp6Q2WJM6WvUctwH+dq/CBH7bogmJnAKsGA9pF0T3RRIUPPs9z4vXrBAIKnaIVLog3UVJWjivGx65drua+hZBI/HwbQr9+/UJ7ZsCvQ7ird707HQ1xjl27djF//nwcDgeqqnLrrbdy5ZVXnvb5mkLYRHHvvfdyyy23MHjw4BorXFVvg3aqrFZrix1HcTID+0Kxw0u5y88zX+4lwWbGWVlGrw5tKDPKOFDmIiM1jn5piTi8AVSPn6uG9qRDvC38yZtIQ4yjOFOuQ3YqdhTjq/BgSbSRmNGW6A6nsf9EC2Cz2XjiiSfo2rUrRUVFTJo0iWHDhhEfH7k9YWHHUfzv//4vUVFRoZ2Gqr+kX7WNtZLeLpYpF3XiUJWXSq+Ox2/QKd5Gh3gb0RaN/Ao3AUNw57BuEZUkIoHrkJ2ir/IIuP2YE6wE3H6KvsrDdcjeaNdszmnm3bp1o2vXrgCkpqaSnJxMWVlZo91rQwhboqioqGDx4sVNEUuLd8l5KSTYTLz+1Q78huDS89vw5AWpmFQFT0AnNdaKKYJGZEaKih3FaFEmTFHByXLV/1bsKD6jUkVLmGa+detW/H5/xA9iDJsofvvb3/L1118zbNiwpoinRVMUhX5pidguSo7YqlIk8lV4MCfU/AXSbCZ8FZ4zOm+kTzM/cuQI9957L0888QSqGtl/QMImijfffJNFixZhNpsxmYJvb83do1LDsyTaCLj9oZIEgO4JYElsvCpac08zdzgczJgxg7vuuouLLrqo0e6zoYRNY1u2bGH37t1s27aNLVu2sGXLFpkkpAaVmNEW3R0g4PYjhCDg9qO7AyRmtG20azbnNHOfz8ef/vQnsrOzueKKKxrtHhtSvZbC+/TTT8nJyQGCQ7gvvfTSRg0KQHeX4C74Cr0qF9VuQu/aDi1KLnV/NoruEEfq8C41ej3aDOzYqL0e1dPMU1NTufDCC2vsPN63b18MIziJZ+DAgTzzzDN1TjN/5ZVX+Mtf/sIjjzxCVlYWuq4zcOBAHnnkEV577TU2btyIqqqcf/75jBgxgjVr1pCTk0NFRQXLly8H4PHHH4/o6mrYaeZPPfUU27ZtIysrC4DVq1eTkZHBPffcc1oXrE9XXMBeQMV3j2PoXlRzDOXFhSSmpBLXayqm+HPQolMjevBXJHfnnoycZi5B3T+zsCWKL774ghUrVoQaWyZMmMD48eNPO1HUh/OXZRgBN6opGsPvBH8Fnvyd+It/wJzcE3NSOnF9bkExRaE7D6KaYlCj20V08pCklqxeVY+qqqrQKkB2e+P0bevuMlz7V+M5vBn33g/RBSiGB4SBGvChm6MRQmCL7Yy/Yi9lX90PigJCB6FjTs4gvs/NqNbIHbQiSS1V2EQxY8YMJkyYwJAhQxBCsGnTJu6+++4GC0AIA+eef1G56TEMbxVGwIXhqQQRQJiiQVFRjAAoCoa3AhFwgmbDk/sxUV3GoMV0QAiBr2wXVTv+SWL/OxssNunMVE8glCJfuLVwwyaKzMxMBg8ezLZt2xBCcM8999C2bcO1Rnvy1+HYsRhD96NEtcUo3Qm6D9DBZwfNAkJHeMpBNeHY/Q6KyYbQ/aEVrxVFwRSbhr9kK7q7VDZ6RgCbzUZpaSkpKSkyWUQ4IQSlpaUndO0eK2yi+M9//sNvfvMbRo0aBQSrIWvXrmX06NENEqBr3ypQTaCoBKoOgN8JVC9trYPuPvpmQDcwvFXgd6Mg8Jf9dLSU4UGLSkEIEAH3Gcclnbm0tDQKCgooLi5u7lCkerDZbKFBZycTNlG8+OKLNVa4io+P58UXX2yQRAEEf/G1aHR7AaJGkqjtAF+whGFNxFfyIyZ3B9CsBCp+RrUmoFrlojCRwGw2h1ayllq+sImiuj/5WLoe5pe5vowAiiUO/+HvEAEvnORaxx0QXAFG6OB3IFQNgUBBIBQzijUJb1EOUZ0vaZj46ingKMWx7RO8BVvRYtsgYrqD7BqUziJhE0Xv3r157LHHuO6661AUhf/3//4fGRkZZ3xhw+egcvOzBBwFBByFwR6McKUJILQXV8CNYk1Ei26HZkvGFNcZYehNnih0RxklH/4PuqcKLTYFX/F+2PYlztQkYnoMb7I4JKkxhR/5nCsAABLjSURBVB3CPW/ePMxmM3fddRezZs3CZrOd0aId1Vy5nxCoOoClTR/MCV3RYtoH2yrqzUD4XZgTumNJyUC1xIPhRzE37S5czl2fo3uqMKd0QbXGYIprAzFtqNq4FBHwN2ksktRYwv5mRkdHN8rgKu/Bb4KDpABzUg98ZT+BNQXch+p5BhUUDV/5bkyx7RGGH8PvJKpT0/4V9x7ciRpdc6cpzDYMnwvdVY4pvl2TxiNJjeFU/oQ3KEXRECLYJmFO7I7uKiJgzwMtCnQPJ273ewzNBooKmhXDXUzAHhynH5N+FeaUM68WhWP4PHgLd2B4HaCZMTyO4GSmykOAAl6BiItBtcY2eiyS1BSaLVHYOo/E+dM7KPFdUVQNU9IF+Cv2IkzRCFcxEKj9YN0HJivmhPOIPvdKYruPxxSbhmpNaPS4faX5HFk2D39JLgiBQMNT8CN4ncF2FpMZ1CiU/pkoZmv4E0pSC9BsiSKqy6X4K/fiK9qMUBQCVfkY3kpAAc0EuqD2xk0FFBNGwElM9wlYUno1erxCCHwluRx69Wb8xXtRoxMxfB78xfuCiUtRg19+H2g+3IXbqdq0nLh+Y1Etcml+qWWrtTFz1qxZoe8XLlxY47WbbrrpjC+saBbiL7yVpIsfxpzUA+EtR7EmBrs+DT9QW1epCpoJS5veWBK7YziLzjiWcETAR/nalzj8xh24936L316Kt3gfgfJ80I82WCoqIIKlCt2HL3cLh9+8g0Ov/wnnjs/w20vwFe/H8EbO6tuSVF+1lihyc3ND3x+/qGhDLQSqKAqKKYpA+W6wxGNWLRjmaAKOgxBw82s7hRL83hyPoqiYUnoT3eV36O4SfOW7ierSuOtjOHd+imvfRnS3PTjeQ9XArx8dH380RkMP9tqIo4lDD6C7q3Dn/oi3YAempI6YYpNBUYkbOInYPpfLoc1Si1FriaKu/8QN+R88YM8DFExRbRCGNzguIioFzNGgWAhWM6zBJKGaUMwx2Nr1BYLDtbXo9g0WS20cOz9Dd9vx5W8DYUDAB0YgWPoJEUdLQr8+VkxW/Ef24C8rwPC7MSV3RotrS+WGt/Dmb230uCWpodRaonC73ezcuRPDMPB4POzcuRMhBEIIPJ4zW/T0WIo5GgGY4rsSqDqA4XehxXYCFAxAM8fi9emY9XIUzYLtnMtQrUnonjIU1URUp6ENFkttdFclvqJfQr00dfbIhAiEqxzQMEwW8HsBUEwWtOgEHNs/wdblwsYKWZIaVK2Jom3btjz22GMAtGnTJvR99eOGYk7sjim6Hbq3AmvHYQQqfiZgz0e1JZEy9FGiOg3np23fcE56Tzx5n+Ep/ArdkY8pNo3YXv+FFt344xRMiR0wfO5ge4RmgYCX+iULAB3hcaAldAg9o5isGK7KRolVkhpD2KXwGtoPP/xwwt4GeEvRCj5AcR1dt9CShN55MiK6MxDcnyE0BVb3BtsBtJijw74bn9i/Hj59EioKgtUN3XfMqyrHN7wKgq0qIYoJMrIgvn0w5qqDcP5IlB4NM7HuTNX4fE+TXPbu7FZriWLTpk11Hjho0KDTumBtWwqKC4diuIsRRgAtpj2K8mvzSXOvv6h3ac+hwq9w/fwNhteJUXXkaPvE0a5clKPJI5hzT5a+TIU5WNU+mOLbYurSgzaX34AWFRmrcUXCloJSZKs1Ubz66qsnff6nn37i8OHDDf6fQ1GUJqlGnA4tJomUy2bjLzuI58DmYPVD94F6tEtU91NrVUTVUKISEZpKoOIQicNuJO7CK1BtctSm1HLUmiheeeWVGo9zcnJ45ZVXaNu2LfPmzWv0wCJN1DkXkXr1ExS8/AcMV0WwYdPQEYYBhuO4HpBjWGKxtjsXoftRbbGYU9JkkpBanLAjMzds2MDf/vY3AGbOnMnQoY3fyxCJdK+L4vcfQK84CJopWL0w2bCkdMZXtBcR8KJYohAe+9HGTgAVc0I7FEsUBFQMjx3FJId1Sy1PrYli3bp1vPLKK8TGxnLXXXedsAFKa1Px5WK8h3aDOQrNEnV0sV8n/iN7j5YqdIS7uiejuoHz1+qI4bFjik/F2lE2+kktT62JYubMmbRv356ePXvyj3/8g3/84x81Xj++anK2c/ywGjU6AdUSTcBRiqKZUEwWdFd5sFfD8AfzglqdJBRQFALOclA1VFWj3cRH5LwPqUWqNVG8/vrrTRlH5Ds6ilyLb4tisaE7KxBCRzFZMbc7H9/B3eB3B4dyK8H5KFpyFxS/l9heI0kefQfWjj2b+y4k6bTUmigGDx5c47Hf7+eXX34hNTWVlJTWtxx+XP9xlPx7IWp0PKot+KXbj6CgEtNzBJrZhq/qCIrQCegKlqgoYjJGY7iraH/tsygmS3PfgiSdtlrnejz00EOhnZrtdjvZ2dnMmTOH8ePHs2rVqiYLMFIkDr+J6O7D0CsOESgvIFBWgGqJJn7QVQifG0uHnpgsVkwJHcAWH1zQxn6EmF6jZJKQWrxaE8XmzZvp3r07AB988AFdu3Zl5cqVLFu2jEWLFjVZgJFCNVvpeMs/6XjLP0m5fDbtJi/gnLmfkXTpzQivEzUqDkun3sFeD2cxpuhEonv8jviBE5s7dEk6Y7VWPcxmc+j7b775ht///vcADbpLWEujqiox6cOISR8Wek6Liid5zJ1Ufvs2qtmGrdsAvFGdScv8E6b41vtZSWeXWhNFXFwcn3/+OampqXz//fc8+uijAAQCgQadPXo2iOo2ANs5/TC8DlRzFFW/7JFJQjqr1JooHnnkERYsWEBJSQkPPPBAqCSxYcMGfve73zVVfC2GoqoRM3dDkhparYmiW7duJ53vMXz4cIYPb9kb2+jeAH67D82qYYq1yJWmJCmMU1pcd8KECSxfvryxYml0fpcP+89lVO4pRUEBIYhqH0ubwZ3QrM22zrAkRbxT+u1o4qUrGkzA6adk80EcBypwFzkwx1mJSYtHtWq4ixyUbTlM29/UvpOzJLV2p5QoLrmkaTf/bQjCEBR9nYff6UP362g2EyJg4MitJO78JEyxVip2leB3+tCsJmK7JhDdMR5FldURSaoWdu/RY5fqnz179gnPRTIhBFX7ynAWVCEMgfAbKJqKatEwAga+Kg+uQ3Y8JU685W68pS6OrC+gbGvjbwEgSS1J2ERx/FL9AF9++WWjBNOQhCEo++EwR77Kx1vmxlVox2/3oruDO5ApCugOP55iJ4YucBXacR20I4DKn0vw2711X0CSWpFaqx5vvfUWb7/9Nvn5+WRlZYWedzqd9O/fv0mCOxOeI06qfinD2iYKX5UHxaSgGCoBhw/dHwABwjDQXQEsibZglUQXeA7b0eKt+Co8mOPk2hGSBHUkiqysLEaMGMEzzzzD3XffHXo+JiaGxMTE2g6LGM6CKhSTgt/uC5YkPPrRbQqPUiDg9qGaNQyfTsDhCzbWagqKy4+h17ZTmSS1PnWOzIyLi+OZZ55pyngajBAGroIqvBVeDH/gxG1MFcAPhl/H59J/XRFXBdWkUfZjEdEd4mS3qSRRjzaKlkpRVHxVvqMJQDlxaezjCwzVuwMaoFo0dG8A+/6KpghVkiLeWZsofJUezHGWYHXDEPXfr0eAEdDxV3mx7ytrsWNHJKkhnbWJQjGpWNtEo8WZT/kuDZ+Or8KLM7eS8u1HGidASWpBztpEEXduErpPR3h0CJziwQYY3gBRHeOo3F2Cr0p2lUqtW2RsKRjG6Wx5ZzgD+L6qwKj0wen8nptA6xEDbgNT92i01PrH3BBb9DUluaWgFE6TN+nXtqVgXU5ny7vSHw5jP0fDkVuJr9yD0I1fGyzroJhVhCIgAFEeM9bEKNqel0ZM5/pPIW/uLRBPldxSUArnrK16BBw+VJOGZtZQTGr95m6oHO0gUVA0BV+lFxSISo1p9HglKZKdtYnC1i4awxvAnGBFtamIk/V8qKBEacHvleCXMAQiYKBaVFRVIblfe1SL1tThS1JEOWtHE8Wek4h9bzm6X0d36SeOmwBUq4YlwYZf9aF7AygCFIuGNSWa6PYxCEMQkyZXrZKkszZRaFYTHS7txqF1+1EBEa2BUIKjNI/u9icCAn+ll6iOsfidPoRfkNAzBQWFgNNHUp92cmSmJHEWJwoAzWYCQ2BKsGKKNuMrc6Oag0vf+Z1eFE1BizGjuwNEt4/D1i4a3RlAs5lI6tOOmC4JzX0LkhQRzupEAaCYTSiqgtANhC5QTMFmGc2sYUuNRYsyg27QeWw65li5UY8kncxZnyjiz0/GmVeJ3xHswTB0A3SBajER2zkB3adjTY6SSUKS6nDW9npUiz0ngeQLU7Em2dCiTAi/gWY1kZCeHBy5GTBI7CX34JCkupz1JQpFVWgzsCPx6Sn4K70EXH5ch+z4q7xYk2wk9mqLNSW6ucOUpIh21ieKapZ4K5b44DDshB6tbzd2SToTZ33VQ5KkMycThSRJYbWI2aNSZPN6vVx00UXNHYbUiJo8UUiS1PLIqockSWHJRCFJUlgyUUiSFJZMFJIkhSUThSRJYclEIUlSWDJRSJIUlkwUkiSFJROFJElhyUQhSVJYEZsoqqqqePPNN2t9fd++fUydOpXs7GyuuOIK5s2bB8DGjRvp0aMHn332Wei9M2bMYOPGjQBMnTqVyy+/nOzsbLKzs7nzzjsb90YiQEFBAStXrqz19WnTpjFw4EBmzJjRhFFJLUnErkdRVVXF22+/zXXXXXfS1x999FFuuOEGRo8eDcBPP/0Ueq19+/a88sorjBw58qTHPvXUU/Tp06fhg45QhYWFrFq1iqysrJO+fvPNN+N2u3n33XebODKppYjYEsXTTz9NXl4e2dnZPPHEEye8fuTIEdq3bx963KNHj9D3PXv2JC4ujvXr15/WtZ1OJ/fffz9ZWVlkZWXx8ccfA4R+2TIzM1m4cGHo/f369WPhwoVMnDiRG2+8ka1btzJ16lRGjRrFp59+CsCyZcu49dZbmTZtGpdffjkvvvhi6PglS5aQmZlJZmYm//znP4FgKeCKK67gwQcfZOzYsdx00014PB4A8vLymDZtGhMnTuTaa69l7969AMydO5cFCxYwZcoURo0axUcffRT6LHNycsjOzg6d/1gXX3wxMTFyNzSpDiJC5efni7Fjx9b6+vvvvy/69+8vpk2bJpYsWSIqKyuFEEJ8++23Yvr06WLTpk3iuuuuE0IIMX36dPHtt98KIYT4r//6L3HZZZeJcePGiXHjxonHH3/8hHM/+eSTYsGCBaHHFRUV4vDhw+KSSy4RpaWlwu/3i6lTp4r//Oc/Qggh0tPTxbp164QQQtx2223ij3/8o/D5fGLXrl1i3LhxQgghPvjgAzF06FBRVlYm3G63GDt2rNi6davYtm2byMzMFE6nUzgcDnHllVeKHTt2iPz8fHHBBReInTt3CiGEuPPOO8WHH34ohBDi+uuvF/v37xdCCPHDDz+IqVOnCiGEmDNnjrjjjjuEruvil19+EaNHj67xmdSlPu+RWq+IrXqEM2nSJIYNG8ZXX33Fp59+yjvvvMO//vWv0OsDBw4EICcn54Rjw1U9NmzYwDPPPBN6nJCQwKZNmxg8eDDJyckAZGVlsWnTJkaPHo3ZbGbEiBEApKenY7FYMJvNpKenU1hYGDrPb3/7W5KSkgAYM2YMmzdvRlEURo8eTXR0dOj5nJwcRo4cSVpaWmjz4IyMDAoLC3E6nWzZsoVZs2aFzuvz+ULfjx49GlVVOf/88ykpKannpylJdWsxieLZZ59l3bp1AKxYsQKA1NRUJk+ezOTJk8nMzOTnn3+ucczMmTN5+eWXMZlO7TaFEChKPTY1PspsNofer6oqFosl9L2u66H3HX9ORVEQdSwHUn0eAE3T8Hq9CCGIj48PfQZ1HSNJDSVi2yhiYmJwOp2hx7Nnz2bFihWhX5Avv/wSv98PQHFxMRUVFaSmptY4x7Bhw6iqqmL37t2ndO2hQ4fyxhtvhB5XVlbSt29fNm3aRFlZGbqus3r1agYNGnRK512/fj0VFRV4PB7Wrl1L//79GTRoEGvXrsXtduNyuVi7dm2oNHQysbGxpKWlsWbNGiCY1MLd3/GfpSSdqogtUSQlJdG/f38yMzMZPnw4c+bMqfH6+vXrefTRR0PL6t177720bduWffv21XjfzJkzue2222o8d88992Cz2ULXOb6B79Zbb+WRRx4hMzMTVVW5/fbbueyyy/jzn//MDTfcgBCCESNGhHpc6mvAgAHcd9995ObmkpWVFar+TJw4kauuugqAyZMn06tXLwoKCmo9z8KFC5k/fz4vv/wygUCAK6+8kp49e9b6/h49eqBpGuPGjQs1uB7r2muvZd++fbhcLkaMGMGjjz7K8OHDT+nepLObXAqviSxbtozt27fz0EMPNXcoknTKIrbqIUlS5JAlCkmSwpIlCkmSwpKJQpKksGSikCQpLJkoJEkKSyYKSZLC+v9OFxtyaSD5TgAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<Figure size 273.6x180 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot_embeddings()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
